package com.bj58.data.emailreports.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.reflect.FieldUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import jxl.Workbook;
import jxl.write.Label;
import jxl.write.Number;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;

import com.bj58.wf.core.WF;
import com.bj58.wf.dal.annotation.Column;
import com.bj58.wf.mvc.thread.BeatContextUtils;

public class ExcelUtil2<T> {

	Class<T> clazz;

	List<String> fieldFilter;

	List<Field> fieldList;

	/**
	 * 每个sheet最大值
	 */
	private static int maxPerSheet = 60000;

	private static Log log = LogFactory.getLog(ExcelUtil2.class);

	private static final File CATALOG = new File(WF.getConfigFolder() + WF.getNamespace() + "/preexcel/");

	public ExcelUtil2(Class<T> clazz) {
		this.clazz = clazz;
	}

	public void setFieldFilter(List<String> filterList) {
		this.fieldFilter = filterList;
	}

	/**
	 * 创建临时文件并返回文件路径
	 * 
	 * @param voList
	 * @param filename文件名
	 * @return
	 * @author guolu 2014-11-17
	 */
	public String createExcelFile(List<T> voList, String filename) {
		if (voList == null || voList.isEmpty()) {
			return null;
		}
		String name = filename + System.currentTimeMillis() + ".xls";
		File excel = new File(CATALOG, name);

		WritableWorkbook wwb = null;
		try {
			wwb = Workbook.createWorkbook(excel);
			int loop = voList.size() / maxPerSheet + 1;
			for (int i = 0; i < loop; i++) {
				createSheet(wwb, i == 0 ? filename : filename + i, i, voList);
			}
		} catch (Exception e) {
			log.info(e);
		} finally {
			if (wwb != null) {
				try {
					wwb.write();
					wwb.close();
				} catch (IOException e) {
				}
			}
			System.gc();
		}
		return excel.getAbsolutePath();
	}

	/**
	 * 创建并下载
	 * 
	 * @param voList
	 * @param filename
	 * @return
	 * @author guolu 2014-11-17
	 */
	public void createExcelFileAndDownload(List<T> voList, String filename) {
		if (voList == null || voList.isEmpty()) {
			return;
		}

		HttpServletResponse response = BeatContextUtils.getCurrent().getResponse();

		setResponseForExcel(response, filename);
		WritableWorkbook wwb = null;

		try {
			long currentfrom = System.currentTimeMillis();
			wwb = Workbook.createWorkbook(response.getOutputStream());
			int loop = voList.size() / maxPerSheet + 1;
			for (int i = 0; i < loop; i++) {
				createSheet(wwb, i == 0 ? filename : filename + i, i, voList);
			}
			long currentto = System.currentTimeMillis();
			log.info("创建时间：" + (currentto - currentfrom));
		} catch (Exception e) {
			log.info(e);
		} finally {
			if (wwb != null) {
				try {
					long currentfrom = System.currentTimeMillis();
					wwb.write();
					wwb.close();
					response.flushBuffer();
					long currentto = System.currentTimeMillis();
					log.info("下载时间：" + (currentto - currentfrom));
				} catch (IOException e) {
				}
			}
			System.gc();
		}
	}

	private void createSheet(WritableWorkbook wwb, String sheetName, int sheetIndex, List<T> voList) throws Exception {
		WritableSheet ws = wwb.createSheet(sheetName, sheetIndex);
		createLableRow(ws);
		int begin = maxPerSheet * sheetIndex;
		int loop = maxPerSheet;
		if (voList.size() > maxPerSheet && (voList.size() / maxPerSheet) == sheetIndex) {
			loop = voList.size() % maxPerSheet;
		}
		for (int i = 1; i <= loop; i++) {
			createDataRow(ws, voList.get(begin + i - 1), i);
			voList.set(begin + i - 1, null);
		}

	}

	/**
	 * 创建lable行
	 * 
	 * @param <T>
	 * @param ws
	 * @param entity
	 * @throws Exception
	 * @author guolu 2014-11-11
	 */
	private void createLableRow(WritableSheet ws) throws Exception {
		if (ws == null) {
			return;
		}
		if (fieldList == null) {
			fillFieldList();
		}
		int i = 0;
		for (Field field : fieldList) {
			String name = field.getName();
			if (field.isAnnotationPresent(Column.class)) {
				name = field.getAnnotation(Column.class).value();
			}
			ws.addCell(new Label(i, 0, name));
			i++;
		}
	}

	private void fillFieldList() {
		fieldList = new ArrayList<Field>();
		if (fieldFilter != null && !fieldFilter.isEmpty()) {

			for (String fieldName : fieldFilter) {
				try {
					Field field = clazz.getDeclaredField(fieldName);
					if (field != null) {
						fieldList.add(field);
					}
				} catch (Exception e) {
					log.info(e);
				}

			}

		} else {
			Field[] fields = clazz.getDeclaredFields();
			for (Field field : fields) {
				fieldList.add(field);
			}
		}

	}

	/**
	 * 创建数据行
	 * 
	 * @param <T>
	 * @param ws
	 * @param entity
	 * @throws Exception
	 * @author guolu 2014-11-11
	 */
	private void createDataRow(WritableSheet ws, T entity, int row) throws Exception {
		if (ws == null || entity == null) {
			return;
		}
		if (fieldList == null) {
			fillFieldList();
		}
		// Field[] fields = entity.getClass().getDeclaredFields();
		int i = 0;
		for (Field field : fieldList) {
			// if (fieldFilter != null && !fieldFilter.contains(field.getName())) {
			// continue;
			// }
			Object value = FieldUtils.readField(field, entity, true);
			if (value == null) {
				value = "--";
			}
			if (field.getType() == int.class || field.getType() == Integer.class) {
				ws.addCell(new Number(i, row, Integer.parseInt(value.toString())));
			} else {
				ws.addCell(new Label(i, row, value.toString()));
			}
			i++;
		}
	}

	private static void setResponseForExcel(HttpServletResponse response, String filename) {
		try {
			filename = URLEncoder.encode(filename, "UTF-8");
			response.setContentType("application/x-download");
			response.addHeader("Content-Disposition", "attachment; filename=" + new String(filename.getBytes("GBK"), "ISO-8859-1") + ".xls");
			response.setContentType("application/vnd.ms-excel;charset=UTF8");
		} catch (UnsupportedEncodingException e) {
			log.info(e);
		}

	}

	public static void downLoad(HttpServletResponse response, String filepath, String filename) {
		if (response == null || filepath == null) {
			return;
		}

		File file = new File(filepath);
		if (!file.exists()) {
			return;
		}
		FileInputStream fis = null;
		setResponseForExcel(response, filename);
		try {
			// channelCopy(Channels.newChannel(new FileInputStream(file)),
			// Channels.newChannel(response.getOutputStream()));
			fis = new FileInputStream(filepath);
			// FileChannel fc = fis.getChannel();
			// MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
			// WritableByteChannel wbc = Channels.newChannel(response.getOutputStream());
			// wbc.write(mbb);
			copy(fis, response.getOutputStream());
			response.flushBuffer();
			// fc.close();

			// file.delete();
		} catch (Exception e) {
			log.info(e);
		} finally {
			try {
				if (fis != null) {
					fis.close();
				}
				file.delete();
			} catch (Exception e) {
				log.info(e);
			}
		}

	}

	public static void copy(InputStream in, OutputStream out) throws IOException {

		byte[] buffer = new byte[64 * 1024 * 1024];
		while (true) {
			int bytesRead = in.read(buffer);
			if (bytesRead == -1) {
				break;
			}
			out.write(buffer, 0, bytesRead);
		}

	}

	private static void channelCopy(ReadableByteChannel src, WritableByteChannel dest) throws IOException {
		ByteBuffer buffer = ByteBuffer.allocateDirect(10 * 1024 * 1024);
		while (src.read(buffer) != -1) {
			// Prepare the buffer to be drained
			buffer.flip();
			// Write to the channel; may block
			dest.write(buffer);
			// If partial transfer, shift remainder down
			// If buffer is empty, same as doing clear( )
			buffer.compact();
		}
		// EOF will leave buffer in fill state
		buffer.flip();
		// Make sure that the buffer is fully drained
		while (buffer.hasRemaining()) {
			dest.write(buffer);
		}
	}

}
